/**
 * Copyright (C) 2011 Audit Logger Project. All rights reserved.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.auditlogger.api;

import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.auditlogger.config.ConfigurationServices;

/**
 * Represents a single record in the log.
 * <p>
 * A "Log Record" is synonymous with "Log Entry", "Log Item", 
 * "Log Event", etc. 
 *  
 * @author <a href="mailto:ravi.sharda@gmail.com">Ravi Sharda</a>
 */
public class LogRecord {
	
	/**
	 * The application domain from which the log message is originated.
	 */
	private static String domain = 
		ConfigurationServices.getPropertiesConfigurator().getAppName();
	
	// This provides a way to "variable state" to this object.  
	//
	// The reason we need to have dynamic properties is that we do not want to put 
	// awkward restrictions on clients regarding what audit entities will be logged 
	// as part of each log event/record. 
	//
	// The +ve is: Allows us to add any properties and at runtime. 
	// 			   Also, fields added by variable state may or may not be added depending 
	//             on the usage.
	// The -ve is: Makes the interface unclear. However, we can work around this by
	// 				using a sub type of this class that provides the application 
    //				specific methods for simpler using. One example is in the DefaultLogEvent. 
	private Map<String, String> dynamicProperties = new HashMap<String, String>();
	
	/**
	 * Date and time this was created (as opposed to when this was actually
	 * logged).
	 */
	private Date currentDateTime = null;
	
	/**
	 * The log message 
	 */
	private String message = null;
	
	/**
	 * The type of event/transaction this record represents. 
	 * <p>
	 * Applications are free to choose event types that make sense in 
	 * their context. Sample sets are:
	 * <ol>
	 * 		<li>request, response, error, unspecified</li> 
	 *      <li>success, failure. unspecified</li>
	 * <ol>
	 */
	@SuppressWarnings("unused")
	private EventType logEventType = null;
	
	private boolean isEntityValid (String name) {
		return true;
	}
	
	/**
	 * Gets the application domain from which the log message is originated. 
	 * @return A string containing the name of the application domain
	 */
	public String getDomain() {
		return domain;
	}

	/**
	 * Gets the <code>Date</code> representing the date/time the record was created
	 * @return The <code>Date</code> the record was created 
	 */
	public Date getLogTime() {
		return currentDateTime;
	}

	/**
	 * 
	 * @return A String containing the message
	 */
	public String getMessage() {
		return message;
	}

	/**
	 * Returns a copy of the variable state associated with the 
	 * <code>LogRecord</code> object. 
	 * <p>
	 * An exception is thrown at runtime if the client tries to 
	 * modify the 
	 * @return
	 */
	public Map<String, String> getDynamicProperties() {
		// prevent clients for modifying the variable state
		final Map<String, String> result = Collections.unmodifiableMap(
				dynamicProperties);
		return result;
	}
	
	public LogRecord (EventType type, String message) {
		this.message = message;
		this.currentDateTime = new Date();
		this.logEventType = type;
	}
	
	/**
	 * We could have offered clients the ability to create objects through
	 * constructors - it would have been a simpler option. However, that would
	 * amount to promising that the name of the class, the package of the class,
	 * and the concrete class of the the object won't change (forever!). 
	 * 
	 * Therefore, the design uses static factories to create the relevant LogRecord 
	 * object. 
	 * 
	 * The downside is it makes the calling code more complex. 
	 */
	
	/**
	 * 
	 * @param message
	 * @return
	 */
	public static LogRecord create (String message) {
		return  new LogRecord (message);
	}
	
	public static LogRecord create (String message, Map<String, String> properties, 
			EventType type) {
		return new LogRecord (message, properties, type);
	}
	
	public static LogRecord create (String message, Map<String, String> properties) {
		return new LogRecord (message, properties);
	}

	/**
	 * Constructs  the LogEvent with the given level and message. 
	 * @param level The Log level
	 * @param message The message
	 */
	private LogRecord (String message) {
		this (EventType.EVENT_TYPE_UNSPECIFIED, message);
	}
	
	private LogRecord (String message, Map<String, String> properties, EventType type) {
		this(message);
	}
	
	private LogRecord (String message, Map<String, String> properties) {
		this(message);
	}
	

	protected boolean addEntity (String name, String value) {
		if (isEntityValid(name)) {
		     dynamicProperties.put(name, value);
		     return true;
		} else {
			throw new InvalidLogEntityException ("Entity with this name is not configured: " 
					+ name); 
		}
	}
	
	protected String getEntity (String key) {
		if (dynamicProperties.containsKey(key)) {
			return dynamicProperties.get(key);
		} else {
			throw new IllegalArgumentException ("Not added " + key); 
		}
	}
}
